package com.lemon.exam.common.util;

import com.lemon.exam.common.properties.ConfigProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.time.Duration;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 *
 * @author Lemon
 * @since 2025/3/21 18:27
 */
@Slf4j
@Component
public class JwtUtil {
    @Resource
    private ConfigProperties configProperties;
    //密钥
    private static volatile SecretKey secretKey;
    private final Object object = new Object();

    /**
     * 获取密钥
     *
     * @return
     */
    private SecretKey getSecretKey() {
        if (secretKey == null) {
            synchronized (object) {
                if (secretKey == null) {
                    secretKey = Keys.hmacShaKeyFor(configProperties.getJwt().getSecret().getBytes());
                }
            }
        }
        return secretKey;
    }

    /**
     * 生成token
     *
     * @param username
     * @return
     */
    public String generateAccessToken(String username) {
        //1.当前时间
        final Date now = new Date();
        //2.过期时间
        Duration duration = Duration.ofMinutes(configProperties.getJwt().getAccessTokenExpirationMinutes());
        final Date expiration = new Date(now.getTime() + duration.toMillis());
        //3.创建JWT Token，并返回
        return buildToken(Collections.singletonMap("username", username), now, expiration, username, username);
    }

    /**
     * 生成刷新token
     *
     * @param username
     * @param password
     * @return
     */
    public String generateRefreshToken(String username, String password) {
        //1.当前时间
        final Date now = new Date();
        //2.过期时间
        Duration duration = Duration.ofMinutes(configProperties.getJwt().getRefreshTokenExpirationMinutes());
        final Date expiration = new Date(now.getTime() + duration.toMillis());
        //3.负载信息
        Map<String, Object> claims = new HashMap<>();
        claims.put("username", username);
        claims.put("password", password);
        //4.创建JWT Token，并返回
        return buildToken(claims, now, expiration, username, username);
    }

    /**
     * 创建token
     *
     * @param claims
     * @param now
     * @param expiration
     * @param subject
     * @param issuer
     * @return
     */
    private String buildToken(Map<String, ?> claims, Date now, Date expiration, String subject, String issuer) {
        return Jwts.builder()
                .header()// 设置头部信息header
                .add("typ", "JWT")
                .add("alg", "HS256")
                .and()
                .claims(claims)//设置自定义负载信息payload
                .id(String.valueOf(System.currentTimeMillis()))//令牌ID，当前时间戳
                .expiration(expiration)//过期日期，要大于签发时间
                .issuedAt(now)//签发时间
                .subject(subject)//主题
                .issuer(issuer)//签发者
                .signWith(getSecretKey(), Jwts.SIG.HS256)//签名
                .compact();
    }

    /**
     * 解析token
     *
     * @param token
     * @return
     */
    public Jws<Claims> parseToken(String token) throws ExpiredJwtException {
        return Jwts.parser().verifyWith(getSecretKey()).build().parseSignedClaims(token);
    }

    /**
     * 根据负载获取载荷信息
     *
     * @param claims
     * @return
     */
    public String getUsername(Jws<Claims> claims) {
        return claims.getPayload().get("username", String.class);
    }
}
